//
//  Interview.swift
//  Interview
//
//  Created by 借悦 on 2019/1/15.
//  Copyright © 2019 apple. All rights reserved.
//  iOS经典问题整理

import UIKit

//MARK: -- 1.内存管理:
class Interview: NSObject {
    //MARK:1. 内存布局?
    /**
     1.见图1.1
     2.stack（栈）:方法调用
     3.heap（堆）:通过alloc等分配的对象
     4.bss:未初始化的全局变量
     5.data:已初始化的全局变量
     6.text程序代码
     */
    
    //MARK:1.2- iOS内存管理机制内部原理?
    /**
     1.taggedPointer，2.NONPOINTER_ISA，3.散列表。
     2.https://blog.csdn.net/Horson19/article/details/82593484
     */
    
    //MARK:1.3- 什么是ARC?
    /**
     0.在Objective-C中采用Automatic Reference Counting （ARC）机制，是编译器特性，让编译器来进行内存管理。ARC是LLVM编译器和Runtime系统相互协作的一个结果,ARC利用LLVM编译器自动帮我们生成release、retain、autorelease代码，像弱引用这样又是利用Runtime在程序运行过程中监控到对象销毁的时候将指向对象的弱引用清空。
     1.自动引用计数器
     2.ARC是LLVM和Runtime协作的结果
     3.ARC中禁止手动调用retain、release、retaincount、dealloc
     4.ARC中新增weak、strong属性关键字
     */
    
    //MARK:1.4 array内存是在什么时候释放的?
    //          - (void)viewDidLoad {
    //              [super viewDidLoad];
    //              NSMutableArray *array = [NSMutableArray array];
    //          }
    /**
     1. 每次runloop循环过程中，都会在将要结束的时候对前一次创建的AutoReleasePool进行pop操作，
        调用AutoReleasePoolPage::pop(),同时会push进来一个新的AutoReleasePool
        所以array是在当次runloop将要结束的时候调用AutoReleasePoolPage::pop()的时候，调用release函数或者方法将其释放
     */
    
    //MARK:1.5 AutoreleasePool的实现原理是怎么样的?
    /**
     1. 是以栈为节点通过双向链表的形式合成的
     2. 是和线程一一对应的
     class AutoreleasePoolPage {
         static size_t const COUNT = SIZE / sizeof(id);
         magic_t const magic;                   检查校验完整性的变量
         id *next;                              指向新加入的autorelease对象
         pthread_t const thread;                当前所在的线程，AutoreleasePool是按线程一一对应的（结构中的thread指针指向当前线程）
         AutoreleasePoolPage * const parent;    父节点 指向前一个page
         AutoreleasePoolPage *child;            child 子节点 指向下一个page
         uint32_t const depth;                  链表的深度，节点个数
         uint32_t hiwat;                        数据容纳的一个上限
     };
     3. AutoReleasePoolPage::push()实现，每次进行AutoreleasePoolPage的创建相当于是不断插入哨兵对象
     4. [obj autorelease]实现，如果next没有指向栈顶就直接把对象添加到next指向的位置，如果指向栈顶。增加一个栈节点到链表上再添加对象
     5. AutoReleasePoolPage::pop()实现，根据传入的哨兵对象找到对应的位置，给上次push操作之后的对象依次发送release消息，回退next指针到正确位置
     */
    
    //MARK:1.6 AutoreleasePool为何可以嵌套使用?
    /**
     1.AutoreleasePool如果没有满的话，就不需要创建AutoreleasePool，满了之后需要创建AutoreleasePool，也就是插入哨兵对象
     */
    
    //MARK:1.7 AutoreleasePool使用场景?
    /**
     1、降低内存使用峰值：
     这一点不用多说，当你使用类似for循环这样的逻辑需要产生大量的中间变量时，Autorelease Pool无意是最佳的一种解决方案；
     for循环里边创建
     2、如果是对NSArray操作，如果可以的话推荐使用OC提供的以下api：
     - (void)enumerateObjectsUsingBlock:
     - (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:
     - (void)enumerateObjectsAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts usingBlock:
     如果你debug一下源码就该知道为什么推荐使用它们了（内部封装了autoreleasepool），我们debug看一下：
     然后在采取以下命令跟踪string_weak_的值变化，如下：
     再点开enumerateObjectsUsingBlock的执行堆栈信息，看一下：
     如果你再debug一下普通的for循环就不会有这些push和pop，既然enumerateObjectsUsingBlock内部有了autoreleasepool，为什么推荐使用它的原因就不多说了。
     原文：https://blog.csdn.net/lizitao/article/details/56485100
     */
    
    //MARK:1.8 循环引用可以分为哪几类?
    /**
     1. 自循环引用:对象A里有个 id __strong obj; 将obj赋值为A原对象就会造成循环引用。
     2. 相互循环引用:对象A强持有对象B,对象B又强持有对象A
     3. 多循环引用:对象A强持有对象B,对象B强持有对象C，对象C强持有A
     */
    
    //MARK:1.9 循环引用考点?
    /**
     1.代理
     2.block
     3.NSTimer
     4.大环引用多循环引用
     */
    
    //MARK:1.10 如何破除循环引用?
    /**
     1. 避免产生循环引用
     2. 在合适的时机手动断开环
     */
    
    //MARK:1.11 避免循环引用的常用方案?
    /**
     1. __weak 对象A强持有对象B,对象B又强持有对象A
     2. __block
        MRC下__block修饰的对象不会增加其引用计数，避免了循环引用
        ARC下__block修饰的对象会被强引用，无法避免循环引用，需手动解环
     3. __unsafe_unretained修饰的对象不会增加其引用计数，避免了循环引用
        如果被修饰对象在某一时机被释放，会产生悬垂指针，再通过指针访问原对象，导致内存泄漏，一般不建议使用
     */
    
    //MARK:1.12 开发过程中是否遇到循环引用，你又是怎么解决的?
    /**
     1. block的使用示例参看block章节
     2. NSTimer的循环引用  见图1.12  使用YYWeakProxy : NSProxy 2.分类实现中间对象弱引用 见1.12.1
     */
    
    //MARK:1.13 为什么weak指针指向的对象在被废弃之后会被自动置为nil?
    /**
     1. 对象被废弃后，在delloc中的内部实现方法会查找是否存在弱引用，如果存在通过哈希查找找到对应的弱引用列表，在clearDeallocating()方法中，会调用一个sidetable_clearDeallocating()的方法，在方法内部会调用两个方法，1.weak_clear_no_lock()这个方法会将每个弱引用表中的指向该对象的弱引用指针，置为nil。2.table.refcnts.erase()方法，这个方法会从引用计数表中，擦除该对象的引用计数。
     原文：https://blog.csdn.net/Horson19/article/details/82593484
    */
}

//MARK: -- 2.runtime
extension Interview {
    //MARK:2.0. runtime的数据结构?
    /**
     1. 见图2.0
     2. cache_t是提供消息传递机制中的缓存方法查找
     3. class_data_bits_t类的一些基本信息，成员变量、属性、方法、分类添加的方法
     https://www.jianshu.com/p/5b7e7c8075ef
     */
    
    //MARK: 2.1 [self class] [self superClass] [super class] [super superClass] 打印分别是什么
    /**
        答:如果self是实例对象打印分别为：self的类对象，self的父类的类对象，self的类对象，self的父类类对象
        如果self是实例对象打印分别为：self的元类对象，self父类的元类对象，self的元类对象，self的父类元类对象
        原因：self调用class方法就是获取当前消息接受者的对象，如果消息接受者是实例对象返回类对象，如果消息接受者是类对象则返回元类对象。
        superClass本质就是返回父类的类对象或者元类对象。
        super其本质就是调用objc_msgSendSuper（self,@selector(class)）。其实就是在发送消息时查找方法是在父类中查找，不在当前类中查找，但是消息接收者还是当前类(self)，所以打印结果和使用self没有区别。
        其实关于本题需要搞清楚两点。1，消息接收者是谁（是谁发送的消息）2，isa指向谁。实例对象的isa->类对象，类对象的isa->元类对象 元类对象的isa->基类的元类对象。如果元类对象已经是基类则指向基类的类对象（NSObject元类对象->NSObject类对象）。
        注意点，若要获取元类对象只能使用object_getClass(类对象)方法。掉用class方法y永远返回的都是类对象
    */
    
    //MARK: 2.2 对象分几种？分别都存了什么？
    /**
     0、见图2.2
     1.对象分为实例对象（instance）、类对象（class）、元类对象（meta-class）
     2.instance实例对象存储了指向类对象的isa，成员变量的值
     3.class类对象存储了指向元类对象的isa，类对象存储方法，协议，属性和成员变量（此处储存了变量名称，类型等）
     4.元类对象的isa指针都指向根元类对象，包括根元类对象自己,存储的是类方法
     引申问题：当我们调用类方法，没有对应的实现，有同名的实例方法的实现，会不会发生崩溃？
     5.元类对象的rootclass的superclass指针指向类对象的rootclass（不会崩溃，当我们调用类方法从根元类对象没查找到对应类方法，它就会找根类对象同名的实例方法实现）
    */
    
    //MARK: 2.3 说一下消息机制
    /**
     0、消息发送流程见图2.3、消息转发流程2.3.2
     1、消息机制主要分三个大步骤。1，消息查找 2，动态方法解析 3，消息转发
     2、消息查找：首先会在消息接收者方法缓存（散列表）中查找方法（之后若查找到方法后会添加到方法缓存中），若查找到了就调用，没有就在方法列表中查找，若没有查找到则会在父类的缓存方法中查找，父类方法缓存中也没有则会在父类中查找。若父类中也没有找到，则会看父类是否是基类，若不是基类则一直查找直到查到方法，若基类中也没有则会进入到动态方法解析步骤。
     3、动态方法解析：首先判断是否解析过，若没有解析过会调用+ (BOOL)resolveClassMethod:(SEL)sel或者+ (BOOL)resolveInstanceMethod:(SEL)sel这两个方法来动态添加方法。之后会重新执行消息查找的流程查找到方法后执行。若没有之前解析过则会跳过动态方法解析执行消息转发步骤。注：动态添加方法的方法是class_addMethod，并且注意添加方法是对象方法还是类方法。
     4、消息转发：进入到消息转发阶段会先去调用- (id)forwardingTargetForSelector:(SEL)aSelector方法，若返回值不为空就会将调用objc_msgSend(返回值,SEL)，若返回值为空或者没有实现则会调用-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法，此方法需要返回一个方法签名，若没有返回方法签名则会调用doesNotRecognizeSelector:方法报错，此时也就结束了消息发送的流程。若反悔了方法签名则会调用- (void)forwardInvocation:(NSInvocation *)anInvocation方法，在这里可以进行最后的处理。
     注：若是类方法的消息转发使用相应+号开头的方法。消息转发的方法提示只有对象方法，类方法没有提示，但是可以调用成功。消息发送和分类无关，分类也是按照上面的流程执行，若分类对转发有实现则会覆盖原来的方法。
     */

    //MARK:2.4 下边打印结果是什么?
    /**
        @interface Phone : Mobile
        @end
        @implementation Phone
        - (instancetype)init {
            self = [super init];
            if self {
                NSLog(@"%@",NSStringFromClass([self Class]));
                NSLog(@"%@",NSStringFromClass([super Class]));
            }
            return self;
        }
        @end
    */
    /**
     1、结果phone phone
     2、调用[self class]会转化为void objc_msgSend(self,@selector(class))
     3、调用[super class]会转化为void objc_msgSendSuper(struct objc_super *super,@selector(class))
     4、struct objc_super {
        _unsafe_unretained id receiver;//接受者receiver就是当前对象
     }
     */
    
    //MARK:2.5 runtime实战演练?
    /**
     1、给分类添加属性（关联对象）
     2、页面统计、事件统计（method_swizzing）使用
     3、实战源码2.5.1
     */
    
    //MARK:2.6 （美团面试题）有没有使用过performSelector,使用,什么时候使用?动态添加方法的时候使用? 为什么动态添加方法?
    /**
     1.为什么动态添加方法? OC都是懒加载,有些方法可能很久不会调用
     2.应用场景：电商,视频,社交,收费项目:会员机制中，只要会员才拥有这些功能
     // 默认OC方法都有两个默认存在的隐式参数,self(哪个类的方法),_cmd(方法编号)
         void run(id self, SEL _cmd, NSNumber *metre) {
            NSLog(@"跑了%@",metre);
         }
     3、什么时候调用:只要调用没有实现的方法 就会调用方法去解决，这里可以拿到那个未实现的方法名
     // 作用:去解决没有实现方法,动态添加方法
         +(BOOL)resolveInstanceMethod:(SEL)sel{
             class:给谁添加方法
             SEL:添加哪个方法
             IMP:方法实现,函数入口,函数名，如：(IMP)run，方法名run强转成IMP
             type: 方法类型：void用v来表示，id参数用@来表示，SEL用:来表示
             class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
             // [NSStringFromSelector(sel) isEqualToString:@"eat"];
             if (sel == @selector(run:)) {
                 // 添加方法
                 class_addMethod(self, sel, (IMP)run,"v@:");
                 return YES;
             }
             return [super resolveInstanceMethod:sel];
         }
     */
    
    //MARK:2.7 其他相关笔试题或面试题?
    /**
     1.[obj foo]和objc_msgSend()函数之间有什么关系？
     2.runtime如何通过Selector找到对应的IMP地址？
         IMP class_getMethodImplementation(Class cls, SEL name);
         IMP method_getImplementation(Method m)
     3.能否向编译后的类中增加实例变量？
     4.能否向动态添加的类中增加实例变量？
         不能向编译后得到的类中增加实例变量；
         能向运行时创建的类中添加实例变量；
         原因如下：
         因为编译后的类已经注册在 runtime 中，类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定，同时runtime 会调用 class_setIvarLayout 或 class_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量；
         运行时创建的类是可以添加实例变量，调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后，objc_registerClassPair 之前，原因同上。
     */
}

//MARK: -- 3.runloop
extension Interview {
    
    /** runloop详解
     1.看慕课知识点 https://blog.csdn.net/weixin_34414196/article/details/86927038
     2.看应用实践 https://www.jianshu.com/p/3105b7f9fb8c
     3.runloop实现预缓存高度 https://www.cnblogs.com/longiang7510/p/5867805.html
    */
    
    //MARK:3.1 什么是runloop?
    /**
     1. runloop是通过内部维护的<事件循环>来对<事件、消息进行管理>的对象
     事件循环
        没有消息处理时，休眠以避免资源占用（用户态->内核态切换），有消息处理时立刻被唤醒（内核态->用户态切换）
     事件循环机制
        1. 在runloop启动之后会发一个通知到obsever,即将启动runloop
        2. 然后发送通知将要处理timer/sourse0事件，处理sourse0
            3.如果有sourse1处理那么就处理唤醒时收到的消息
            4.如果没有sourse1处理，线程将要休眠同时发送通知给obsever,内部发生了用户态到内核态的切换，线程正式进入休眠，等待唤醒
                唤醒runloopde't的条件1.sourse1 2.timer事件 3.外部手动唤醒
            5.线程刚被唤醒，发送通知到observer线程被唤醒了，处理缓醒时收到的消息，回到2
        6. 杀死程序就会产生runloop退出，发送通知即将退出runloop,runloop退出也就销毁了
     
     */

    //MARK:3.2 main为什么能保持一直运行状态不退出?
    /**
     1. 在Main函数中调用的UIApplicationMain内部会启动主线程的runloop，runloop是对时间循环的一种维护机制，没有消息处理时，休眠以避免资源占用（用户态->内核态切换），有消息处理时立刻被唤醒（内核态->用户态切换）
     */
    
    //MARK:3. runloop的数据结构?
    /**
     CFRunLoopRef：对外部暴露的对象，外界通过CFRunLoopRef的接口来管理整个Runloop。
     CFRunLoopModeRef：
     CFRunLoopSourceRef
     CFRunLoopTimerRef
     CFRunLoopObserverRef
     nsrunloop对应多个mode，mode对应多个sourse/timer/observer
     NSRunLoopCommonModes:不是实际存在的一种mode,是同步sourse/timer/observer到多个mode中的一种技术方案
     */
    
    //MARK:3.3 如何将一个事件源同步添加到多个mode中?
    /**
     1.CFRunLoopAddTimer(runloop, timer, NSRunLoopCommonModes)
     */
    
    //MARK:3.4 怎么实现一个常驻线程?
    /** 见源码3.4
     1. 为当前线程开启一个runloop
     2. 向该runloop中添加一个port/sourse等维持runloop的事件循环
     3. 启动runloop
     */

    //MARK:3.5 runloop与线程有怎样的关系?
    /**
     1. 一一对应的关系
     2. 创建线程默认不开启runloop
     */
    
    //MARK:3.6 怎么保证子线程数据回来更新ui的时候不打断用户的滑动操作?
    /**
     1.
     */
    
    //MARK:3. ?
    /**
     1.
     */
}

//MARK: -- 5.数据库
extension Interview {
    
}

//MARK: -- 6.UI相关
extension Interview {
    //MARK:6.1-UITableView重用机制原理?
    /**
     重用实现分析
     　　　　查看UITableView头文件，会找到NSMutableArray*  visiableCells，和NSMutableDictnery* reusableTableCells两个结构。visiableCells内保存当前显示的cells，reusableTableCells保存可重 用的cells。
     
     　　　　TableView显示之初，reusableTableCells为空，那么tableView dequeueReusableCellWithIdentifier:CellIdentifier返回nil。开始的cell都是通过 [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]来创建，而且cellForRowAtIndexPath只是调用最大显示cell数的 次数。
     
     　　　　比如：有100条数据，iPhone一屏最多显示10个cell。程序最开始显示TableView的情况是：
     
     　　　　1. 用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]创建10次cell，并给cell指定同样的重用标识(当然，可以为不同显示类型的 cell指定不同的标识)。并且10个cell全部都加入到visiableCells数组，reusableTableCells为空。
     
     　　　　2. 向下拖动tableView，当cell1完全移出屏幕，并且cell11(它也是alloc出来的，原因同上)完全显示出来的时候。cell11加入到 visiableCells，cell1移出visiableCells，cell1加入到reusableTableCells。
     
     　　　　3. 接着向下拖动tableView，因为reusableTableCells中已经有值，所以，当需要显示新的 cell，cellForRowAtIndexPath再次被调用的时候，tableView dequeueReusableCellWithIdentifier:CellIdentifier，返回cell1。cell1加入到 visiableCells，cell1移出reusableTableCells；cell2移出visiableCells，cell2加入到 reusableTableCells。之后再需要显示的Cell就可以正常重用了。
     
     　　　　所以整个过程并不难理解，但需要注意正是因为这样的原因：配置Cell的时候一定要注意，对取出的重用的cell做重新赋值，不要遗留老数据。
     
     一些情况
     
     　　　　使用过程中，我注意到，并不是只有拖动超出屏幕的时候才会更新reusableTableCells表，还有：
     
     　　　　1. reloadData，这种情况比较特殊。一般是部分数据发生变化，需要重新刷新cell显示的内容时调用。在 cellForRowAtIndexPath调用中，所有cell都是重用的。reloadData调用后，把visiableCells中所有 cell移入reusableTableCells，visiableCells清空。cellForRowAtIndexPath调用后，再把 reuse的cell从reusableTableCells取出来，放入到visiableCells。
     
     　　　　2. reloadRowsAtIndex，刷新指定的IndexPath。如果调用时reusableTableCells为空，那么 cellForRowAtIndexPath调用后，是新创建cell，新的cell加入到visiableCells。老的cell移出 visiableCells，加入到reusableTableCells。于是，之后的刷新就有cell做reuse了。
     */
    
    //MARK:6.2-如何解决UITableView在多线程环境下修改、访问数据源的问题?
    /**
     1. 并发访问 & 数据拷贝 图(6.2.1)
     2. 串行访问 图(6.2.2)
     */
    
    //MARK:6.3-UIView和CALayer的区别?
    /**
     1. UIView为CALayer提供显示内容，以及负责处理触摸等事件，参与响应链。
     2. CALayer负责显示内容contents
     引申问题，为什么这么设计?
     1. 系统设计的一个原则，单一职责原则
     */
    
    //MARK:6.4-点击图片(6.4.1)空白区域,系统是如何找到响应者是view c2的？视图响应顺序是什么？如果最后传递到UIApplicationDelegate 依然没有响应，会发生什么?
    /**
     1. 响应者联调见图6.4.2
     2. hitTest内部实现见图6.4.3
     3. 正确顺序是 C2 -> B2 -> A 到 UIApplicationDelegate 依然没有任何视图处理该事件的时候，直接忽略此事件， 并不会崩溃。
     */
    
    //MARK:6.5-点击图片(6.5.1)指定区域接受响应事件如何实现？
    /**
     1. 见6.5.2源码
     */
    
    //MARK:6.6-图像显示原理？
    /**
     1. 见6.6图片
     */
    
    //MARK:6.7-UI卡顿、掉帧的原因？
    /**
     见6.7图片
     每一秒会有60帧的画面更新，在16.7ms内下一个vsync信号到来之前CPU和GPU没有完成下个画面的合成所以就会产生掉帧，造成卡顿。
     1.UI渲染需要时间较长，无法按时提交结果。
     2.一些需要密集计算的处理放在了主线程中执行，导致主线程被阻塞，无法渲染UI界面。
     3.网络请求由于网络状态的问题响应较慢，UI层由于没有模型返回无法渲染。
     */
    
    //MARK:6.8-滑动优化方案？
    /**
     1. CPU:
        1. 对象创建、调整、销毁可以放到子线程
        2. 预排班（布局计算、文本计算）
        3. 预渲染（文本等异步绘制、图片编解码）
     2. GPU
        1. 纹理渲染（layer圆角、阴影蒙层等触发离屏渲染的优化、CPU异步绘制减轻GPU压力）
        2. 视图混合 (视图层级较多也会增加GPU压力，CPU异步绘制减少层级)
     3. UITableView的优化技巧－异步绘制Cell
        1. https://www.cnblogs.com/tangyuanby2/p/6763543.html
     */
    
    //MARK:6.9-UIView绘制原理,异步绘制？
    /**
     1. https://www.jianshu.com/p/a0ef0838e0e0
     */
    
    //MARK:6.10-关于离屏渲染的理解 以及解决方案？
    /** 离屏渲染会增加GPU的工作量，造成cpu和GPU不能再16.7秒内完成画面的合成，造成卡顿和掉帧。
     1. https://www.cnblogs.com/tangyuanby2/p/6763543.html
     */
}

//MARK: -- 7.网络
extension Interview {
    
    //MARK:7.1- 什么是http？
    // 直接看博客 https://www.jianshu.com/p/9894a978327b
    /** 超文本传输协议
     1. 请求报文
            方法  URL  协议版本  CRLF(首部字段区)
            首部字段名:值 CRLF
            CRLF 首部字段名:值 CRLF
            实体主体(post)
        响应报文
            版本  状态码  短语  CRLF(首部字段区)
            首部字段名:值 CRLF
            CRLF 首部字段名:值 CRLF
            实体主体(post)
        常用的请求方式
            GET POST HEAD PUT DELETE OPTIONES
        GET、POST方式的区别
            一般回答
                1.get请求参数以？分割到URL后边，post请求参数放在body里边
                2.get参数长度限制2048个字符，post一般没有长度限制
                3.get请求不安全、post请求比较安全
            标准答案
                GET：获取资源
                    安全的:不应该引起sever端的任何变化（get、head、options都是安全的）
                    幂等的:同一个方法执行多次和执行一次的效果完全一致
                    可缓存的:请求可以被缓存（get、head)
                post：处理资源
                    非安全的  非幂等的  不可缓存的
        你都了解那些状态码，他们的含义是什么？
            1XX
            2XX 响应成功
            3XX 发生了一些可能存在网络的重定向
            4XX 客户端发起的请求可能存在某些问题
            5XX sever可能存在异常
     2. 连接建立流程
        三次握手
            1、客户端发送一个SYN同步报文到sever
            2、sever收到连接建立的报文后，会返回客户端一个同步ACK报文到客户端
            3、客户端再回应一个同步报文
        http请求、http响应
        四次挥手断开连接
            客户端发送FIN、sever回应一个断开报文给客户端
            合适的时机sever再次发送一个断开的报文给客户端，客户端回个sever一个确认报文
     3. http的特点
        无连接：有一个建立连接和断开连接的过程
            http的持久连接方案
                头部字段
     
        无状态：比如说多次发送请求的时候，如果同一个用户，sever是不知道的
            cookie/session
     */
    
    //MARK:7.2- HTTPS和HTTP有怎样的区别？？
    /**
     1、https协议需要到ca申请证书，一般免费证书较少，因而需要一定费用。
     2、http是超文本传输协议，信息是明文传输，https则是具有安全性的ssl加密传输协议。
     3、http和https使用的是完全不同的连接方式，用的端口也不一样，前者是80，后者是443。
     4、http的连接很简单，是无状态的；HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议，比http协议安全。
     */
    
    //MARK:7.3-HTTPS连接建立流程是怎样的？
    /**
         客户端发送报文，包含TLS版本号，加密算法，随机数C
         服务端发送加密算法、随机数S，和证书
         客户端验证服务器证书
         客户端组装会话秘钥
         客户端发送报文，服务器端的公钥对预主秘钥进行加密传输
         服务端通过私钥解密得到预主秘钥
         服务端组装会话秘钥
         客户端发送加密握手消息
         服务器端发送加密握手消息
     
         会话秘钥： 随机数S + 随机数C + 预主秘钥
     HTTPS都使用了哪些加密算法？
         连接建立过程使用非对称加密，非对称加密很耗时。
         后续通信使用了对称加密。
     非对称加密
         非对称加密算法是一种密钥的保密方法。 非对称加密算法需要两个密钥：公开密钥（publickey）和私有密钥（privatekey）。 公开密钥与私有密钥是一对，如果用公开密钥对数据进行加密，只有用对应的私有密钥才能解密；如果用私有密钥对数据进行加密，那么只有用对应的公开密钥才能解密。
     对称加密
         采用单钥密码系统的加密方法，同一个密钥可以同时用作信息的加密和解密，这种加密方法称为对称加密，也称为单密钥加密。
     */
    
    //MARK:7.4- 是否了解UDP数据报协议？
    /**
     特点
         1.无连接
         2.尽最大努力交付
         3.面向报文
     功能
        1.复用 - 发送方多端口复用传输层数据报UDP->ip层进行传输
        2.分用 - 接收方接受IP数据报数据拆分成UDP数据报然后根据端口号分发给端口123
        3.差错检测 - 放入到8字节UDP首部的末尾，接收方以同样的方法计算，然后比较进行简单的差错检测（图7.4）
     */
    
    //MARK:7.5- 什么是TCP传输控制协议？
    /**
     特点
        1.面向连接 - 传输之前需要建立连接（三次握手），传输结束之后需要释放连接（四次挥手）
            为什么要进行三次握手？
                解决同步请求链接超时的问题，重传产生的问题，比如说两次客户端发送超时又重新上传了一个和超时报文都到达服务器，服务器会返回给客户端一个确认ACK,客户端并没有给服务端重复的确认报文，那么建立的链接就只有一个
            为什么进行四次挥手？
                全双工，客户端和服务器在一条通道上都可以进行发送和接收，客户端发送断开服务器确认后，属于半关闭状态，服务器发送断开请求，客户端确认才真正断开
        2.可靠传输
            保证报文无差错、不丢失、不重复、按序到达
            可靠传输是通过停止等待协议来实现的
                无差错情况(差错校验)
                超时重传（发送失败，重新发送）
                确认丢失（服务端接收到报文，确认丢失，超时重传报文，服务器接收到相同报文，丢弃重传报文，发送确认消息）
                确认迟到（和确认丢失一样，如果迟到的回调又回来了那么客户端可以什么都不做）
        3.面向字节流
            TCP是按照字节进行传输的，比如说发送方发送十个字节，可能会分成两次传输一次3一次7，按照实际情况，进行拆分
        4.流量控制
            流量控制是通过滑动窗口协议来实现的（见图7.5.4）
        5.拥塞控制
            慢开始、拥塞避免（见图7.5.5）
            快恢复、快重开
                达到网络拥塞，重新回到新的门限值，线性增长
     */
    
    //MARK:7.6- 今日头条、美团腾讯面试题-了解DNS解析吗？是怎么一个过程?
    /**
     1.是域名到IP地址的映射，DNS解析请求采用UDP数据报，且是明文
        客户端想要请求数据，先通过DNS协议向DNS服务器发送一个域名解析请求，DNS服务器返回客户端一个解析完成的IP，客户端再通过IP请求服务器
     */
    
    //MARK:7.7- DNS解析查询方式？
    /**
     1. 递归查询(7.7.1)
     2. 迭代查询(7.7.2)
     */
    
    //MARK:7.8- DNS存在哪些常见的问题？
    /**
     1.DNS劫持问题
        DNS劫持和HTTP的关系是怎么样的？
            没有关系，1.DNS解析发生在HTTP建立连接之前，2.DNS解析请求使用的是UDP数据报，端口53
        怎么解决DNS劫持问题？
            HTTPDNS
                使用HTTP协议向DNS服务器的80端口进行请求
            长连接（7.8.1）
     2.DNS转发问题(7.8.2)
     */
    
    //MARK:7.9- 什么是cookie？
    /**
     1. cookie主要用来记录用户状态，区分用户，状态保存在客户端,客户端向服务器发送请求，服务生成Cookie同响应数据一同返回给客户端，客户端保存Cookie。
     修改Cookie？
         新Cookie覆盖旧Cookie。
         覆盖时新Cookie字段要与原Cookie一致。
         设置Cookie的expires=过去的时间点，或者maxAge=0，可以删除Cookie。
     怎么保证cookie的安全？
        对cookie进行加密处理
        只在https上携带cookie
        设置cookie为httpOnly,放置跨站脚本攻击
     */
    
    //MARK:7.10- 什么是session？
    /**
     1. session也是用来记录用户状态，区分用户，状态保存在服务器端。
     */
    
    //MARK:7.11- session和cookie的关系是怎么样的？
    /**
     1.session需要依赖于cookie机制
     */
    
    //MARK:7.12- 网络相关面试总结？
    /**
     1. HTTP中get和post方式有什么区别？
     2. HTTPS连接建立流程是怎么样的？
     3. TCP和UDP有什么区别？
     4. 请简述TCP的慢开始过程？
     5. 客户端怎么避免DNS劫持？
     */
}

//MARK: -- 8.架构和框架
extension Interview {
    //MARK:8.1- 如何设计一个图片缓存框架？
    /**
     1. https://www.jianshu.com/p/49eeb040b820
     */
    
    //MARK:8.-？
    /**
     1.
     */
}

//MARK: -- 9.设计模式模式相关
extension Interview {
    //MARK:9.1- 你都了解哪些设计原则？
    /**
     1. 单一职责原则
        一个类只负责一件事
     2. 开闭原则
        对修改关闭，对扩展开放
     3. 接口隔离原则
        提供多个专门的协议，而不是一个庞大臃肿的协议，协议中的方法应当尽量少
     4. 依赖倒置原则
        抽象不应该依赖于具体实现，具体实现可以依赖于抽象
     5. 里氏替换原则
        父类可以被子类无缝替换，且原有功能不受影响
     6. 迪米特法则
        一个对象应该对其他对象有尽可能少的了解
        高内聚、低耦合
     */
    
    //MARK:9.2- 责任链，一个需求变更的问题 先执行业务A -> B -> C,产品经理让修改为 C -> B -> A？
    /**
     1.责任链模式是一条链，链上有多个节点，每个节点都有各自的责任。当有输入时，第一个责任节点看自己能否处理该输入，如果可以就处理。如果不能就交由下一个责任节点处理。依次类推，直到最后一个责任节点。
     */
    
    //MARK:9.3- 桥接，一个关于业务解耦的问题，一个列表有三种接口请求方式并存，列表和多套数据耦合的问题？
    /**
     1. 代码9.3
     */
    
    //MARK:9.4- 适配器，一个现有类需要适应变化的问题？
    /**
     1.
     */
    
    //MARK:9.5- 单例+命令模式？
    /**
     1.
     */
    
    //MARK:9.6- ？
    /**
     1. 手写单例
     2. 你都知道那些设计原则，请谈谈你的理解
     3. 能否用一幅图简单的表示桥接模式的主要结构
         A1                         B1
         A2 -> classA -> classB <-  B2
         A3                         B3
     4. ui事件传递机制是怎么实现的，你对其中运用到的设计模式有怎样的理解？
     5.
     */
    
    //MARK:9.-？
    /**
     1.
     */
}

//MARK: -- 11.性能优化相关
extension Interview {
    
}

//MARK: -- 12.耗电优化相关
extension Interview {
    
}

//MARK: -- 13.objective-c语言相关
extension Interview {
    //MARK:13.1-什么是分类(category)，分类都用来干什么？
    /**
     定义
     1.Objective-C类别也叫分类，是一种不需要继承即可给类添加方法的语法技术。
     特点
     1.运行时决议，运行时runtime才将分类添加的内容，添加到相关类上。
     2.可以为系统类添加分类。（UIview、NSObject）
     分类可以添加什么
     1.实例方法、类方法、协议、属性（通过runtime添加属性）
     使用的情形
     1.扩展已有的类
     2.私有方法公开化
     3.分解体积庞大的类文件（按功能分解比较大的类）
     类别的局限
     1.只能添加方法，不能直接添加属性。在类别中声明的属性，将无法存取。（通过runtime可以添加属性）
     2.类别中的方法，会覆盖父类中的同名方法，无法再调用父类中的方法（因为类别中无法使用super），为防止意外覆盖，总是应该给类别加上前缀。
     3.不同文件中的同名类别，同名方法，不会报错，实际执行的方法以最后一个编译的文件为准，因此使用前缀防止类互相覆盖。
     分类的加载调用栈
     1._objc_init (runtime初始化)
     2.map_2_images (images意思是镜像)
     3.map_iamges_nolock
     4._read_images (读取内存中的相关镜像，也就是可执行文件)
     5.remethodizeClass (分类内部加载逻辑都从这里开始)
     */

    //MARK:13.2-用关联对象技术为分类添加成员变量，成员变量添加到那里去了，是否添加到了分类对应成员变量数组上去了？
    /**
     1.https://blog.csdn.net/olsQ93038o99S/article/details/80796125
     2.见13.2代码
     */
    
    //MARK:13.3-一般用扩展做什么？
    /**
     1.声明私有属性 2.声明私有方法 3.声明私有成员变量
     */
    
    //MARK:13.4-分类和扩展的区别?
    /**
     1.分类运行时决议，扩展是编译时决议
     2.分类可以有声明有实现，扩展只以声明的形式存在，实现写在宿主类.m中
     3.可以为系统类添加分类，不能为系统类添加扩展
     */
    
    //MARK:13.5-代理?
    /**
     1. 代理准确的说是一种设计模式
     2. iOS当中以@protocol形式体现
     3. 传递方式一对一
     */
    
    //MARK:13.5-NSNotification?
    /**
     1. 是使用观察者模式来实现用于跨层传递消息的机制
     2. 传递方式一对多
     */
    
    //MARK:13.6-怎么实现通知机制?
    /**
     1.管理一个NSNotification_Map
     2.notificationame对应observer_list(observer1,observer2,observer3...)
     */
    
    //MARK:13.7-什么是kvo?
    /**
     1.kvo是objective-c对观察者设计模式的一种实现
     2.Apple使用了isa混写（isa-swizzling）来实现kvo
     isa混写（isa-swizzling）是怎么实现的？
     1.看图（13.7）
     2. - (void)setValue:(id)obj { //重写A的NSKVONotify_A的setter实现
            [self willChangeValueForKey:@"keyPath"];
            // 调用父类不影响原类实现
            [super setValue:obj];
            // 通过下边方法通知所有观察者
            [self didChangeValueForKey:@"keyPath"];
        }
     */
    
    //MARK:13.8-修改监听对象属性使kvo生效的方法?
    /**
     1.使用setter方法改变值kvo才能生效
     2.使用setValue:forKey改变kvo才能生效
     3.成员变量直接修改（需要手动添加kvo）才能生效
         - (void)change { // 修改成员变量上下加代码
             [self willChangeValueForKey:@"keyPath"];
             _obj = 1;
             [self didChangeValueForKey:@"keyPath"];
         }
     4.直接改变成员变量不能触发kvo
     */
    
    //MARK:13.9-kvc?
    /**
     1.kvc就是键值编码技术
        - (id)valueForKey:(NSString*)key;//直接通过Key来取值
        - (void)setValue:(nullableid)value forKey:(NSString*)key;//通过Key来设值
     */
    
    //MARK:13.10-kvc键值编码是否有违背于面向对象编程思想，为什么?
    /**
     1.valueForKey和setValue:forKey，key没有任何的限制，在知道私有成员变量名字的时候可以
     在外界通过这两个方法取值，赋值，从这个层面上讲有违背，面向对象编程
     */
    
    //MARK:13.11-valueForKey内部实现原理?
    /**
     1.看图13.11、13.11.2
     */
    
    //MARK:13.12-MRC下如何重写retain修饰变量的setter方法?
    /**
     1.@property (nonatomic,retain) id obj;
         - (void)setObj:(id)obj {
            if (_obj != obj) { // 为什么做这个判断
                [_obj release];
                _obj = [obj retain];
            }
         }
     2. 如果不判断传过来的是同一个值，直接release释放,这个值就空了，再retain就会异常
     */
}

//MARK: -- 14.block相关
extension Interview {
    //MARK:14.1 什么是block?
    /**
     1.block是将<函数>及其<执行上下文>封装起来的<对象>.
     2.写一个block函数用命令 clang -rewrite-objc JYBlock.m 生成一个JYBlock.cpp C++文件 查看具体实现
     */
    
    //MARK:14.2什么是block调用?
    /**
     1. block调用就是函数调用
     */
    
    //MARK:14.3 滴滴出行截获变量,下边代码打印结果?
    /**
     int multiplier = 6;
     int(^Block)(int) = ^int(int num) {
         return num * multiplier;
     };
     multiplier = 4;
     NSLog(@"result is %d",Block(2));
     1. 下边可能出来的引申问题
        clang -rewrite-objc -fobjc-arc MCBlock.m;
     2. 局部变量->基本数据类型
        result is 12
        对基本数据类型的局部变量截获其值
     3. 局部变量->对象数据类型
        对于对象类型的局部变量<连同所有权修饰符>一起截获
     4. 局部静态变量
        修改static int multiplier = 6;
        result is 8
        是以指针形式截获局部静态变量
     5. 全局变量
     6. 静态全局变量
        不截获全局变量和静态全局变量
     */
    
    //MARK:14.4 什么时候会用到__block修饰符?
    /**
     1. 一般情况下，对被截获对象进行赋值操作需要加__block修饰符;
     2. 赋值 != 使用
     3. 局部变量基本数据类型、对象数据类型需要使用__block修饰符;
     4. 静态局部变量、静态全局变量、全局变量不需要__block修饰符;
     */
    
    //MARK:14.5 __forwarding存在的意义?
    /**
     0. 栈copy到堆上  全局变量copy什么也不做  堆上copy增加引用计数
     1. 不论在任何内存位置，都可以顺利访问到同一个__block变量
     2. 栈block经过copy到堆上，栈上和堆上的__forwarding都指向堆上的__block变量
     */
    
    //MARK:14.6 下边代码会不会产生循环引用？
    /**
     __block MCBlock *blockSelf = self;
     _block = ^int(int num) {
         return num * blockSelf.var;
     };
     _block(3);
     1. 在mrc下不会产生循环引用
     2. 在arc下会产生循环引用，引起内存泄漏
     3. 解决方案
     __block MCBlock *blockSelf = self;
     _block = ^int(int num) {
         int res = num * blockSelf.var;
         blockSelf = nil;
         return res;
     };
     如果很长时间不调用block就无法解除循环引用
     */
    
    //MARK:14.为什么block会产生循环引用?
    /**
     1. 对象持有block，block使用了对象的成员变量自引用 __weak解除
     2. __block在arc下产生的循环引用，通过断环破除循环引用。
     */
}

//MARK: -- 15.多线程与锁相关面试题
extension Interview {
    
    //MARK:15.1 字节跳动面试题，下边代码会产生什么问题?
    /**
     问题一
     - (void)viewDidLoad {
         [super viewDidLoad];
         dispatch_sync(dispatch_get_main_queue(), ^{
             [self doSomething];
         });
     }
     1. 产生死锁
     2. 死锁原因:如果在主线程中运用主队列同步，首先分派viewDidLoad到主线程中，在执行过程中需要调用block，当block同步调用完成之后viewDidLoad才能向下走，block想要向下执行依赖主队列的一个性质先进先出，所以block想要执行依赖viewDidLoad完成，产生相互等待死锁
     问题二下边代码是否有问题
     - (void)viewDidLoad {
         [super viewDidLoad];
         // 创建新的串行队列
         dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
         dispatch_sync(queue, ^{
             [self doSomething];
         });
     1. 没有问题
     2. 如果在主线程中运用创建新的串行队列同步，首先分派viewDidLoad到主线程中，在执行过程中需要调用block，block是同步任务，所以在当前线程执行也就是主线程，当block执行完毕后再去执行viewDidLoad
     }
     问题三
     如果viewDidLoad和block都在一个串行队列调用，那么就会产生和问题一一样的问题
     */
    
    //MARK:15.2 美团新零售面试题，下面代码打印结果?
    /**
     //问题一 同步并发队列
     NSLog(@"1");
     dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
     dispatch_sync(global_queue, ^{
         NSLog(@"2");
         dispatch_sync(global_queue, ^{
             NSLog(@"3");
         });
         NSLog(@"4");
     });
     NSLog(@"5");
     1. 12345
     问题二 改成自定义串行队列会有什么问题
     dispatch_queue_t global_queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
     1.死锁，同15.1 答案一
     */
    
    //MARK:15.3 腾讯面试真题，下边代码打印结果?
    /**
     - (void)viewDidLoad {
         [super viewDidLoad];
         dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
         dispatch_async(global_queue, ^{
             NSLog(@"1");
             [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
             NSLog(@"3");
         });
     }
     - (void)printLog {NSLog(@"2");}
     1. 打印结果:13
     2. 通过异步方式分派到全局并发队列，block会在GCD底层维护的线程池中的某一个线程中去进行执行处理，这些线程默认是没有开启runloop的，performSelector即使是延迟0秒也是需要创建任务提交到runloop的，没有runloop的话performSelector会失效的。
     */
    
    //MARK:15.4 美团、滴滴怎么实现多读单写？
    /**
     1.见源码 15.4
     // 异步栅栏调用设置数据
     dispatch_barrier_async(concurrent_queue, ^{
         // 做写入操作
     });
     */
    
    //MARK:15.5 爱奇艺使用GCD实现需求:A、B、C三个任务并发完成后执行D?
    /**
     1. 见源码实现15.5 testThread、testThread2实现，建议使用testThread2
     2. dispatch_group_enter、dispatch_group_leave、dispatch_group_notify实现
     */
    
    //MARK:15.6 NSOperation去实现多线程有什么优势和特点?
    /**
     1. 添加任务依赖
     2. 任务执行状态控制
     3. 控制最大并发量
     */
    
    //MARK:15.7 我们可以控制NSOperation的那些执行状态?
    /**
     如果只重写main方法，底层控制变更任务执行完成状态，以及任务退出。
     如果重写了start方法，自行控制任务状态
     1. isReady:是否就绪
     2. isExecuting:是否正在执行过程中
     3. isFinished:是否执行完成
     4. isCancelled:是否已取消
     */
    
    //MARK:15.8 系统是怎么移除一个isFinished=YES的NSOperation的?
    /**
     1. 是通过kvo的方式来移除NSOperationQueue中NSOperation的，来达到销毁NSOperation的效果
     */
    
    //MARK:15.9 NSThread实现原理?
    /**
     1. start()之后经过一系列异常判断，内部创建了一个pThread线程，同时指定线程的启动函数，在启动函数中会发送一个通知通知线程已经启动，调用main()函数，main()函数中会调用performselector方法在SEL方法中我们可以维护事件循环，来达到常驻线程的目的
     */
    
    //MARK:15.10 日常开发中都是用过那些锁，都是怎么使用的?
    /**
     1. @synchronized
        一般在创建单利的时候使用，来保证在多线程环境下，创建的对象是唯一的
     2. atomic
        修饰属性关键字
        对被修饰对象进行原子操作（赋值操作线程安全，使用的时候需要自己做线程安全的保护）
     3. OSSpinLock:自旋锁
        循环等待访问，不释放当前的资源
        用于轻量级的数据访问，简单的int+1-1操作
        runtime源码中对引用计数加减操作使用到自旋锁
     4. NSLock 蚂蚁金服下面代码会出什么问题，怎么解决?
         self.lock = [[NSLock alloc] init];
         - (void)methodA {
             [_lock lock];
             [self methodB];
             [_lock unlock];
         }
         - (void)methodB {
             [_lock lock];
             // dosomething
             [_lock unlock];
         }
        答案：死锁，换成递归锁
        self.lock = [[NSRecursiveLock alloc] init];
     5. dispatch_semaphore_t
        dispatch_semaphore_create(1)
            内部有一个结构体里边有两个值 信号量的数值VALUE 线程列表LIST
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            内部实现是先对信号量减一，减一后信号量小于0的话，没有资源可以访问，就会将自己阻塞起来，是个主动行为
        dispatch_semaphore_signal(semaphore);
            内部实现是先对信号量加一，加一后信号量小于等于0的话，意味着在释放信号之前有队列在排队，由释放信号的线程唤醒被阻塞线程实现线程同步，是个被动行为
     */
    
    //MARK:15.11 iOS系统为我们提供的几种多线程技术各自特点是怎么样的?
    /**
     •NSThread:
     –优点：NSThread 比其他两个轻量级，使用简单
     –缺点：需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销
     •NSOperation：
     –不需要关心线程管理，数据同步的事情，可以把精力放在自己需要执行的操作上
     –NSOperation是面向对象的
     •GCD：
     –Grand Central Dispatch是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用，是替代NSThread， NSOperation的高效和强大的技术
     –GCD是基于C语言的
     */

}

